package com.clp.util.hutool.core.map;

import com.clp.util.hutool.core.lang.Editor;
import com.clp.util.hutool.core.util.ObjectUtil;
import com.clp.util.hutool.core.util.ReflectUtil;

import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class MapUtil {
    /**
     * 创建Map<br>
     * 传入抽象Map{@link AbstractMap}和{@link Map}类将默认创建{@link HashMap}
     *
     * @param <K>     map键类型
     * @param <V>     map值类型
     * @param mapType map类型
     * @return {@link Map}实例
     */
    @SuppressWarnings("unchecked")
    public static <K, V> Map<K, V> createMap(Class<?> mapType) {
        if (mapType.isAssignableFrom(AbstractMap.class)) {
            return new HashMap<>();
        } else {
            return (Map<K, V>) ReflectUtil.newInstance(mapType);
        }
    }

    /**
     * Map的键和值互换
     * 互换键值对不检查值是否有重复，如果有则后加入的元素替换先加入的元素<br>
     * 值的顺序在HashMap中不确定，所以谁覆盖谁也不确定，在有序的Map中按照先后顺序覆盖，保留最后的值
     *
     * @param <T> 键和值类型
     * @param map Map对象，键值类型必须一致
     * @return 互换后的Map
     * //@see #inverse(Map)
     * @since 3.2.2
     */
    public static <T> Map<T, T> reverse(Map<T, T> map) {
        return filter(map, (Editor<Map.Entry<T, T>>) entry -> new Map.Entry<T, T>() {
            @Override
            public T getKey() {
                return entry.getValue();
            }

            @Override
            public T getValue() {
                return entry.getKey();
            }

            @Override
            public T setValue(T value) {
                throw new UnsupportedOperationException("Unsupported setValue method!");
            }
        });
    }

    /**
     * 过滤<br>
     * 过滤过程通过传入的Editor实现来返回需要的元素内容，这个Editor实现可以实现以下功能：
     *
     * <pre>
     * 1、过滤出需要的对象，如果返回null表示这个元素对象抛弃
     * 2、修改元素对象，返回集合中为修改后的对象
     * </pre>
     *
     * @param <K>    Key类型
     * @param <V>    Value类型
     * @param map    Map
     * @param editor 编辑器接口
     * @return 过滤后的Map
     */
    public static <K, V> Map<K, V> filter(Map<K, V> map, Editor<Map.Entry<K, V>> editor) {
        if (null == map || null == editor) {
            return map;
        }

        final Map<K, V> map2 = ObjectUtil.clone(map);
        if (isEmpty(map2)) {
            return map2;
        }

        map2.clear();
        Map.Entry<K, V> modified;
        for (Map.Entry<K, V> entry : map.entrySet()) {
            modified = editor.edit(entry);
            if (null != modified) {
                map2.put(modified.getKey(), modified.getValue());
            }
        }
        return map2;
    }

    public static boolean isEmpty(Map<?, ?> map) {
        return null == map || map.isEmpty();
    }

    public static boolean isNotEmpty(Map<?, ?> map) {
        return null != map && !map.isEmpty();
    }

    public static <K, V> Map<K, V> toCamelCaseMap(Map<K, V> map) {
        return (map instanceof LinkedHashMap) ? new CamelCaseLinkedMap<>(map) : new CamelCaseMap<>(map);
    }
}
